package org.qrd.openmayyam;

//import java.io.ByteArrayInputStream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import java.util.HashMap;

//import java.util.Hashtable;


/**
 * A custom class loader that loads servlets on the fly
 *
 * @author Adarsh
 * @author Sivaraman
 */
public class CustomClassLoader extends ClassLoader {



    /**
     * classPath contains the CLASSPATH system variable
     */
  //  private String classPath;

    /**
     * main() method is used to Test this class loader as a standalone application
     */

//    public static void main(String[] args) {

//        CustomClassLoader cl = new CustomClassLoader();
//        //Class cls = cl.getClass("/home/adarsh/sample/WEB-INF/classes/mypackage/Hello.class", "mypackage.Hello");
//        Class cls = cl.getClass("D:\\QRD\\deployer\\wars\\sample\\WEB-INF\\classes\\mypackage\\Hello.class", "mypackage.Hello");
//        try {
//            HttpServlet hs = (HttpServlet) cls.newInstance();
//        } catch (InstantiationException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        } catch (IllegalAccessException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
//    }

    /**
     * Returns the class requested for
     *
     * @param classPath The classPath system variable
     * @param className The className is the complete class name the user requested for
     */

    protected Class<?> getClass(String classPath,String className) 
    {
    	try
    	{    	
    	
    return !classPath.equals("classPath")?loadClass(className,classPath):loadClass(className,true);
    	}
    	catch(ClassNotFoundException  e)
    	{
    		System.out.println("getclass exception");
    	}
		return null;
    	
    }

    /**
      * Search for the classname in the system classpath
      * 
      * @param className
      */
     protected Class<?> loadClass(String className,boolean resolve) throws ClassNotFoundException {
    	 System.out.println("loadclass1");
    	 
    	
    	 Class<?> result = null;
         try {
        	
             result = CustomClassLoader.class.getClassLoader().loadClass(className);
             return result;
         }
         catch (ClassNotFoundException e) {
             System.out.println(className + " Not Found in current directory");
         }
         try {
       	  System.out.println("findsystemclass1");
          return findSystemClass(className);
         } catch (Exception e) {}
         

      return findClass(className);
     }

     /**
      * Search for the classname in the given classpath.
      * 
      * @param className
      * @param classpath
      * @return
      * @throws ClassNotFoundException
      */
     protected Class<?> loadClass(String className, String classPath)
       throws ClassNotFoundException {
    	 System.out.println("load class2");
    
    	 Class<?> result = null;
         try {
        	
             result = CustomClassLoader.class.getClassLoader().loadClass(className);
             return result;
         }
         catch (ClassNotFoundException e) {
             System.out.println(className + " Not Found in current directory");
         }
         try {
       	  System.out.println("findsystemclass2");
          return findSystemClass(className);
         } catch (Exception e) {}

    		
      return classPath.equals("classPath") ? findClass(className) : findClass(
        className, classPath);
     }

     /**
      * Returns the classfile found in the systempath, else null.
      * 
      * @param className
      */
     protected Class<?> findClass(String className) {
    	 byte classByte[];
         Class<?> result = null;
         result = (Class<?>) classes.get(className);
         if (result != null) {
          return result;
         }
      
      
      try {
       String classPath = ((String) ClassLoader.getSystemResource(
         className.replace('.', File.separatorChar) + ".class")
         .getFile()).substring(1);
       System.out.println("classpath"+classPath);
       classByte = loadClassData(classPath);
       result = defineClass(className, classByte, 0, classByte.length,null);
       classes.put(className, result);
       System.out.println("hashtable classname 1"+classes);
       //System.out.println();
     //  classes.put(classPath, result);
       //System.out.println("hashtable classpath 1"+classes);
       return result;
      } catch (Exception e) {
       return null;
      }

     }

     /**
      * Returns the classfile. First find in the cache, else, search in the classpath
      * file, else find in the system path.
      * 
      * @param className
      */
     protected Class<?> findClass(String className, String classPath) {
      byte classByte[];
      Class<?> result = null;
      result = (Class<?>) classes.get(className);
      if (result != null) {
       return result;
      }
      
      try {
          classByte = loadClassData(classPath);
       result = defineClass(className, classByte, 0, classByte.length,
         null);
       classes.put(className, result);
       System.out.println("hashtable classname 2"+classes);
       //classes.put(classPath, result);
       //System.out.println("hashtable classpath 2"+classes);
        return result;
      } catch (Exception e) {

      }

      try {
    	  System.out.println("findsystem");
       return findSystemClass(className);
      } catch (Exception e) {
       return null;
      }

     }

     public void removeAllClasses() {
    	 System.out.println("removeall");
      classes.clear();
     }

     public void removeClassEntry(String className) {
    	 System.out.println("removeclass");
      classes.remove(className);

     }

     private byte[] loadClassData(String classPath) throws IOException {

    	 File f = new File(classPath);
         long len = f.length();
         byte raw[] = new byte[(int) len];
         FileInputStream fis = new FileInputStream(f);
         int r = fis.read(raw);
         if (r != len)
             System.out.println("cant read");
         fis.close();
         return raw;

     }

     @Override
     protected void finalize() throws Throwable {

      // TODO Auto-generated method stub
      super.finalize();
      classes.clear();
      classes = null;
     }
     /*
      * Below hashmap can be set as WeakHashMap provided, the library is in the
      * classpath. Below is the issue, if the class is not in the classpath.
      * 
      * If the library is not in the classpath, then it would cause an exception.
      * If the library is in the classpath findSystemClass would find the class
      * for you and problem is solved.
      */
     private HashMap<String, Class<?>> classes = new HashMap<String, Class<?>>();
    }